<template>
  <el-card class="data-card" shadow="never">
    <div slot="header" class="card-header">
      <div class="header-left">
        <i class="el-icon-s-help" />
        <span>校区投保率统计</span>
      </div>
      <div class="header-right">
        <el-date-picker v-model="localQueryForm.expirationDate" type="date" placeholder="选择到期日" format="yyyy-MM-dd" value-format="yyyy-MM-dd" size="small" style="width: 160px; margin-right: 8px;" @change="handleFilterChange" />
        <el-button size="small" icon="el-icon-refresh" :loading="loading" @click="refreshData">刷新</el-button>
        <el-button size="small" icon="el-icon-download" @click="exportData">导出</el-button>
      </div>
    </div>

    <!-- 交叉表格：管理区域 × 品牌（总数/购买保险数/投保率）以及行汇总 -->
    <div v-loading="loading" class="guidance-table-container">
      <el-table :data="pivotRows" size="small" stripe border show-summary :summary-method="getSummaries" sum-text="总计" class="report-table">
        <!-- 行标题：管理区域 -->
        <el-table-column prop="managementArea" label="管理区域" min-width="160" align="center">
          <template #default="scope">
            <span class="data-primary">{{ scope.row.managementArea }}</span>
          </template>
        </el-table-column>

        <!-- 动态品牌分组列（每个品牌三列） -->
        <el-table-column v-for="brand in sortedBrandList" :key="brand" :label="getBrandLabel(brand)" align="center">
          <el-table-column :prop="brandProp(brand, 'totalCampus')" label="校区总数" min-width="110" align="center">
            <template #default="scope">
              <span class="data-info">{{ scope.row[brandProp(brand, 'totalCampus')] || 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column :prop="brandProp(brand, 'insuredCampus')" label="购买数" min-width="140" align="center">
            <template #default="scope">
              <span class="data-success">{{ scope.row[brandProp(brand, 'insuredCampus')] || 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column :prop="brandProp(brand, 'insuredRate')" label="投保率" min-width="120" align="center">
            <template #default="scope">
              <span class="data-primary">{{ formatRate(scope.row[brandProp(brand, 'insuredRate')]) }}</span>
            </template>
          </el-table-column>
        </el-table-column>

        <!-- 行汇总列分组 -->
        <el-table-column label="汇总" align="center">
          <el-table-column prop="summaryTotalCampus" label="校区总数" min-width="110" align="center">
            <template #default="scope">
              <span class="data-info">{{ scope.row.summaryTotalCampus || 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="summaryInsuredCampus" label="购买数" min-width="140" align="center">
            <template #default="scope">
              <span class="data-success">{{ scope.row.summaryInsuredCampus || 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="summaryInsuredRate" label="投保率" min-width="120" align="center">
            <template #default="scope">
              <span class="data-primary">{{ formatRate(scope.row.summaryInsuredRate) }}</span>
            </template>
          </el-table-column>
        </el-table-column>
      </el-table>
    </div>
  </el-card>
</template>

<script>
import request from '@/utils/request'
/**
 * 组件：InsuranceReport
 * 功能：调用后端接口 `/api/report/Services/insurance-statistics` 展示校区投保率统计（品牌 × 管理区域）。
 * 约定：
 * - 仅统计状态为“正常/筹备”的校区，接口返回字段遵循 camelCase。
 * - 显示接口返回的 rows（brand, managementArea, totalCampus, insuredCampus, insuredRate）与 summary。
 * - 时间展示使用北京时间（CST）。
 * 最后更新（CST）：以前端构建时间为准。
 */
export default {
  name: 'InsuranceReport',

  props: {
    /**
     * 外部查询参数（可选）。
     * 当父组件传入非空对象时，将以该对象为查询条件并自动刷新数据。
     */
    queryForm: {
      type: Object,
      default: () => ({})
    },
    /**
     * 管理区域选项，用于排序
     */
    managementAreaOptions: {
      type: Array,
      default: () => []
    }
  },

  data() {
    return {
      // 组件内查询表单（用于本组件内的查询交互）
      localQueryForm: {
        expirationDate: ''
      },
      // 加载状态
      loading: false,
      // 明细行
      rows: [],
      // 汇总信息
      summary: { totalCampus: 0, insuredCampus: 0, insuredRate: 0 },
      // 北京时间展示
      updateTimeCst: '',
      // 品牌选项
      brandOptions: []
    }
  },

  computed: {
    /**
     * 判断父组件的查询参数是否有效（非空对象）。
     * @returns {boolean}
     */
    hasExternalQuery() {
      return this.queryForm && Object.keys(this.queryForm).length > 0
    },

    /**
     * 排序后的品牌列表（根据sortCode排序）
     * @returns {string[]} 品牌名称数组
     */
    sortedBrandList() {
      const brandMap = new Map()

      this.brandOptions.forEach(option => {
        if (option && option.enabledMark === 1) {
          const sc = Number(option.sortCode)
          const sortCode = isNaN(sc) ? 999 : sc
          const code = String(option.enCode || '')
            .trim()
            .toLowerCase()
          brandMap.set(code, { name: code, sortCode })
        }
      })

      this.rows.forEach(r => {
        if (r && r.brand) {
          const brandName = String(r.brand)
            .trim()
            .toLowerCase()
          if (!brandMap.has(brandName)) {
            brandMap.set(brandName, { name: brandName, sortCode: 999 })
          }
        }
      })

      return Array.from(brandMap.values())
        .sort((a, b) => a.sortCode - b.sortCode)
        .map(item => item.name)
    },

    /**
     * 交叉表行数据：以管理区域为行，品牌为列，最后附加行汇总。
     * 扁平化属性命名规则：`<brand>__<field>`，field ∈ { totalCampus, insuredCampus, insuredRate }。
     * 更新日期（CST）：2025-11-12
     * @returns {Array<Object>} 交叉表的行数据
     */
    pivotRows() {
      return this.buildPivotRows(this.rows, this.sortedBrandList)
    }
  },

  mounted() {
    // 初次加载：优先使用父组件传入的查询参数，否则使用本地表单
    if (this.hasExternalQuery) {
      this.syncLocalFormFromProp()
    }
    this.loadBrandOptions()
    this.loadData()
  },

  watch: {
    /**
     * 监听外部查询参数变化，自动同步并重新加载。
     */
    queryForm: {
      handler() {
        if (this.hasExternalQuery) {
          this.syncLocalFormFromProp()
          this.loadData()
        }
      },
      deep: true
    },
    /**
     * 监听管理区域选项变化，重新排序数据
     */
    managementAreaOptions: {
      handler() {
        // 管理区域选项变化时，重新构建数据（触发排序）
        if (this.managementAreaOptions && this.managementAreaOptions.length > 0) {
          // 这里不需要重新加载数据，只需要重新排序
          // 由于sortedBrandList和pivotRows都是计算属性，会自动重新计算
        }
      },
      immediate: true,
      deep: true
    }
  },

  methods: {
    getBrandLabel(code) {
      const key = String(code || '')
        .trim()
        .toLowerCase()
      const opt = this.brandOptions.find(
        o =>
          String(o.enCode || '')
            .trim()
            .toLowerCase() === key
      )
      return (opt && opt.fullName) || code
    },
    handleFilterChange() {
      this.loadData()
      this.$emit('filter-change', this.localQueryForm)
    },
    async loadBrandOptions() {
      try {
        const res = await this.$store.dispatch('base/getDictionaryData', {
          sort: 'Brand'
        })
        this.brandOptions = res || []
      } catch (error) {
        this.brandOptions = []
        this.$message.error('品牌字典加载失败')
      }
    },

    /**
     * 生成品牌字段属性键名，便于在表格中通过 prop 访问。
     * 更新日期（CST）：2025-11-12
     * @param {string} brand 品牌名称
     * @param {('totalCampus'|'insuredCampus'|'insuredRate')} field 字段名
     * @returns {string} 形如 "<brand>__<field>" 的属性键
     */
    brandProp(brand, field) {
      return `${brand}__${field}`
    },

    /**
     * 构建交叉表数据：按管理区域分组，每个品牌生成三列数据，并计算行汇总。
     * 根据sortCode对管理区域和品牌进行排序
     * 更新日期（CST）：2025-11-12（北京时间）
     * @param {Array<Object>} rows 原始明细行（含 brand, managementArea, totalCampus, insuredCampus, insuredRate）
     * @param {string[]} brandList 品牌列表（已按sortCode排序）
     * @returns {Array<Object>} 生成后的交叉表行数据
     */
    buildPivotRows(rows, brandList) {
      // 按管理区域分组
      const areaMap = new Map()
      rows.forEach(r => {
        if (!r || !r.managementArea) return
        const area = String(r.managementArea)
        if (!areaMap.has(area)) areaMap.set(area, [])
        areaMap.get(area).push(r)
      })

      const result = []
      areaMap.forEach((list, area) => {
        const rowObj = { managementArea: area }
        let sumTotal = 0
        let sumInsured = 0

        // 以品牌列表为准生成列，缺失的数据按 0 处理
        brandList.forEach(brand => {
          const found =
            list.find(
              x =>
                String(x.brand || '')
                  .trim()
                  .toLowerCase() === String(brand)
            ) || null
          const total = found ? Number(found.totalCampus || 0) : 0
          const insured = found ? Number(found.insuredCampus || 0) : 0
          const rate = found ? Number(found.insuredRate || 0) : 0

          rowObj[this.brandProp(brand, 'totalCampus')] = total
          rowObj[this.brandProp(brand, 'insuredCampus')] = insured
          rowObj[this.brandProp(brand, 'insuredRate')] = rate

          sumTotal += total
          sumInsured += insured
        })

        // 行汇总（投保率按合计后计算）
        const summaryRate = sumTotal > 0 ? (sumInsured / sumTotal) * 100 : 0
        rowObj.summaryTotalCampus = sumTotal
        rowObj.summaryInsuredCampus = sumInsured
        rowObj.summaryInsuredRate = Number(summaryRate.toFixed(2))

        result.push(rowObj)
      })

      // 按管理区域的sortCode排序
      return result.sort((a, b) => {
        const areaA = this.getManagementAreaSort(a.managementArea)
        const areaB = this.getManagementAreaSort(b.managementArea)
        return areaA - areaB
      })
    },

    getManagementAreaSort(managementArea) {
      if (!managementArea) return 999
      let area = this.managementAreaOptions.find(item => item.fullName === managementArea)
      if (!area) {
        area = this.managementAreaOptions.find(item => item.enCode === managementArea)
      }
      const sortCode = area ? area.sortCode || 999 : 999
      const numericsortCode = Number(sortCode)
      return isNaN(numericsortCode) ? 999 : numericsortCode
    },
    /**
     * 从外部 props 同步到本地查询表单。
     * 避免直接修改 props，保持单向数据流。
     */
    syncLocalFormFromProp() {
      this.localQueryForm = {
        brand: this.queryForm.brand || '',
        company: this.queryForm.company || '',
        provinceId: this.queryForm.provinceId || ''
      }
    },

    /**
     * 加载投保率统计数据（品牌 × 管理区域）。
     * 请求参数：brand、company、provinceId（可选）。
     * 响应结构：{ rows: Array, summary: { totalCampus, insuredCampus, insuredRate } }
     */
    async loadData() {
      this.loading = true
      this.$emit('loading', true)

      try {
        const params = this.getQueryParams()
        // 说明：本项目统一使用 utils/request，GET 参数通过 data 传递，拦截器会自动映射为 params
        const res = await request({
          url: '/api/report/Services/insurance-statistics',
          method: 'get',
          data: params
        })

        // 兼容后端统一响应（code/msg/data）或直接数据返回
        const payload = res && res.data != null ? res.data : res || {}

        // 验证数据格式
        if (!payload.rows || !Array.isArray(payload.rows)) {
          console.warn('投保率统计接口返回数据格式异常:', payload)
          this.rows = []
          this.summary = { totalCampus: 0, insuredCampus: 0, insuredRate: 0 }
        } else {
          this.rows = payload.rows
          this.summary = payload.summary || { totalCampus: 0, insuredCampus: 0, insuredRate: 0 }
        }

        this.updateTimeCst = this.getNowInCst()

        // 触发数据更新事件
        this.$emit('data-updated', {
          rows: this.rows,
          summary: this.summary,
          queryParams: params
        })
      } catch (err) {
        console.error('获取投保率统计失败：', err)

        // 更友好的错误提示
        let errorMsg = '获取投保率统计失败，请稍后重试'
        if (err.message) {
          if (err.message.includes('timeout')) {
            errorMsg = '请求超时，请检查网络连接'
          } else if (err.message.includes('404')) {
            errorMsg = '接口不存在，请联系技术支持'
          } else if (err.message.includes('500')) {
            errorMsg = '服务器错误，请稍后重试'
          }
        }

        this.$message.error(errorMsg)

        // 重置数据
        this.rows = []
        this.summary = { totalCampus: 0, insuredCampus: 0, insuredRate: 0 }
      } finally {
        this.loading = false
        this.$emit('loading', false)
      }
    },

    /**
     * 刷新数据。与 loadData 一致，便于用户快速获取最新统计。
     */
    refreshData() {
      this.loadData()
    },

    /**
     * 重置查询表单并重新加载数据。
     */
    resetForm() {
      this.localQueryForm = { expirationDate: '' }
      this.loadData()
    },

    /**
     * 组织查询参数。保持与后端接口字段一致（camelCase）。
     * 当父组件传入非空对象时优先使用父组件参数，否则使用本地表单。
     * @returns {{brand?: string, company?: string, provinceId?: string}}
     */
    getQueryParams() {
      const src = this.hasExternalQuery ? this.queryForm : this.localQueryForm
      return {
        expirationDate: src.expirationDate || undefined
      }
    },

    /**
     * 将百分比数值格式化为带 % 的字符串，保留两位小数。
     * @param {number} val 百分比数值，如 83.33
     * @returns {string} 例如 "83.33%"
     */
    formatRate(val) {
      if (val === null || val === undefined) return '0%'
      const num = Number(val)
      return `${num.toFixed(2)}%`
    },

    /**
     * 获取当前北京时间（CST）字符串显示。
     * @returns {string} 例如 "2025-11-11 14:30:00"
     */
    getNowInCst() {
      const formatter = new Intl.DateTimeFormat('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
        timeZone: 'Asia/Shanghai'
      })
      // Intl 格式为 "YYYY/MM/DD HH:MM:SS"，统一替换斜杠
      return formatter.format(new Date()).replace(/\//g, '-')
    },

    /**
     * 导出当前统计数据为 CSV 文件。
     * 文件包含明细行与汇总信息，便于线下分析。
     */
    exportData() {
      // 构建交叉表头
      const baseHeaders = ['管理区域']
      const brandHeaders = []
      this.sortedBrandList.forEach(brand => {
        const label = this.getBrandLabel(brand)
        brandHeaders.push(`${label}-校区总数`)
        brandHeaders.push(`${label}-购买数`)
        brandHeaders.push(`${label}-投保率(%)`)
      })
      const summaryHeaders = ['汇总-校区总数', '汇总-购买数', '汇总-投保率(%)']
      const headers = baseHeaders.concat(brandHeaders).concat(summaryHeaders)

      const lines = [headers.join(',')]
      // 交叉数据行输出
      this.pivotRows.forEach(row => {
        const line = [row.managementArea || '']
        this.sortedBrandList.forEach(brand => {
          const total = row[this.brandProp(brand, 'totalCampus')] || 0
          const insured = row[this.brandProp(brand, 'insuredCampus')] || 0
          const rate = row[this.brandProp(brand, 'insuredRate')] || 0
          line.push(total)
          line.push(insured)
          line.push(Number(rate).toFixed(2))
        })
        line.push(row.summaryTotalCampus || 0)
        line.push(row.summaryInsuredCampus || 0)
        line.push(Number(row.summaryInsuredRate || 0).toFixed(2))
        lines.push(line.join(','))
      })

      // 追加全局汇总说明行（可选）：保留原有 summary 便于对齐
      lines.push('')
      lines.push(['全局汇总', ...Array(this.sortedBrandList.length * 3).fill(''), this.summary.totalCampus || 0, this.summary.insuredCampus || 0, Number(this.summary.insuredRate || 0).toFixed(2)].join(','))

      const csv = '\ufeff' + lines.join('\n')
      const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `投保率统计_${this.updateTimeCst}.csv`
      a.click()
      URL.revokeObjectURL(url)
    },

    /**
     * 提供导出数据给父组件（保持与旧组件约定的接口形态）。
     * @returns {Object} 导出所需的明细与汇总及查询条件
     */
    getExportData() {
      return {
        rows: this.rows,
        summary: this.summary,
        queryParams: this.getQueryParams()
      }
    },

    /**
     * 计算表格汇总行
     * @param {Object} param - 表格参数 { columns, data }
     * @returns {Array} 汇总行数据
     */
    getSummaries(param) {
      const { columns, data } = param
      const sums = []
      columns.forEach((column, index) => {
        if (index === 0) {
          sums[index] = '总计'
          return
        }
        const prop = column.property
        if (!prop) {
          sums[index] = 'N/A'
          return
        }
        if (prop.endsWith('__totalCampus') || prop === 'summaryTotalCampus') {
          const total = data.reduce((acc, row) => acc + Number(row[prop] || 0), 0)
          sums[index] = total
          return
        }
        if (prop.endsWith('__insuredCampus') || prop === 'summaryInsuredCampus') {
          const insured = data.reduce((acc, row) => acc + Number(row[prop] || 0), 0)
          sums[index] = insured
          return
        }
        if (prop.endsWith('__insuredRate')) {
          const brand = prop.split('__')[0]
          const totalKey = `${brand}__totalCampus`
          const insuredKey = `${brand}__insuredCampus`
          const sumTotal = data.reduce((acc, row) => acc + Number(row[totalKey] || 0), 0)
          const sumInsured = data.reduce((acc, row) => acc + Number(row[insuredKey] || 0), 0)
          const rate = sumTotal > 0 ? ((sumInsured / sumTotal) * 100).toFixed(2) : '0.00'
          sums[index] = `${rate}%`
          return
        }
        if (prop === 'summaryInsuredRate') {
          const sumTotal = data.reduce((acc, row) => acc + Number(row['summaryTotalCampus'] || 0), 0)
          const sumInsured = data.reduce((acc, row) => acc + Number(row['summaryInsuredCampus'] || 0), 0)
          const rate = sumTotal > 0 ? ((sumInsured / sumTotal) * 100).toFixed(2) : '0.00'
          sums[index] = `${rate}%`
          return
        }
        const values = data.map(item => Number(item[prop]))
        if (!values.every(value => isNaN(value))) {
          sums[index] = values.reduce((prev, curr) => {
            const value = Number(curr)
            return isNaN(value) ? prev : prev + value
          }, 0)
        } else {
          sums[index] = 'N/A'
        }
      })
      return sums
    }
  }
}
</script>

<style lang="scss" scoped>
@import './shared-table-styles.scss';
.insurance-report {
  .report-card {
    margin-bottom: 16px;
  }

  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
  }

  .card-title {
    font-size: 16px;
    font-weight: 600;
    color: #303133;
  }

  .header-actions {
    display: flex;
    gap: 8px;
  }

  .query-form {
    margin-bottom: 8px;
  }

  .update-time {
    font-size: 12px;
    color: #909399;
    margin-bottom: 8px;
  }

  .summary {
    display: flex;
    gap: 24px;
    padding: 12px 0;
    margin-top: 8px;
    border-top: 1px solid #f0f2f5;
  }

  .summary-item {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .summary-label {
    color: #606266;
  }

  .summary-value {
    color: #303133;
    font-weight: 600;
  }
}
</style>
